﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary;

namespace SmartMathLibrary.NumericalIntegration
{
    /// <summary>
    /// This class provides an integration for cubic splines.
    /// </summary>
    [Serializable]
    public class FullCubicSplineIntegrator : AbstractSplineIntegrator
    {
        /// <summary>
        /// This field holds the spline to integrate.
        /// </summary>
        private ICubicSpline integral;

        /// <summary>
        /// Initializes a new instance of the <see cref="FullCubicSplineIntegrator"/> class.
        /// </summary>
        /// <param name="integral">The spline to integrate.</param>
        public FullCubicSplineIntegrator(ICubicSpline integral)
        {
            this.integral = integral;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FullCubicSplineIntegrator"/> class.
        /// </summary>
        /// <param name="integral">The spline to integrate.</param>
        public FullCubicSplineIntegrator(CubicSpline integral)
        {
            if (integral == (CubicSpline) null)
            {
                throw new ArgumentNullException("integral");
            }

            this.integral = integral;
        }

        /// <summary>
        /// Gets or sets the spline to integrate.
        /// </summary>
        /// <value>The spline to integrate.</value>
        public ICubicSpline Integral
        {
            get { return integral; }
            set { integral = value; }
        }

        /// <summary>
        /// Integrates the spline in a numerical way.
        /// </summary>
        /// <returns>The value of the intergral.</returns>
        public override double Integrate()
        {
            double result = 0;

            foreach (CubicSplineComponent component in this.integral.Components)
            {
                result += InternalIntegrate(component);
            }

            return result;
        }

        private static double InternalIntegrate(CubicSplineComponent component)
        {
            SimplePolynomial polynomial = new SimplePolynomial(4);

            polynomial.SetValueAtRank(0, 0);
            polynomial.SetValueAtRank(1, component.Polynomial.ValueAtRank(0));
            polynomial.SetValueAtRank(2, component.Polynomial.ValueAtRank(1) / 2);
            polynomial.SetValueAtRank(3, component.Polynomial.ValueAtRank(2) / 3);
            polynomial.SetValueAtRank(4, component.Polynomial.ValueAtRank(3) / 4);

            return polynomial.SolveAt(Math.Max(component.Start.X, component.End.X)) -
                   polynomial.SolveAt(Math.Min(component.Start.X, component.End.X));
        }
    }
}